import os, string
from chmod import chmod
import vcs
from vcs import TEST, TRUE, FALSE, NL, SYSTEM, WINDOWS, UNIX
from vcs import NULLDEV, DELIMITER
from rlog import Rlog

#TEST=1

_last_rep_file		= ''
_last_log_with_history	= FALSE
_last_log		= None

# ---- log handling ----------------------------------

def log_without_history(use_cache=FALSE):
	global _last_rep_file, _last_log_with_history, _last_log
	if use_cache and _last_rep_file == vcs.repositoryFile:
		_last_log.reuse()
		return _last_log
	else:
		if string.find(vcs.repositoryFile, ' ') > 0:	# multiple files
			return Rlog(vcs.repositoryFile, vcs.workingFile, '-h')
		_last_rep_file = vcs.repositoryFile
		_last_log_with_history = FALSE
		_last_log = Rlog(vcs.repositoryFile, vcs.workingFile, '-h')
		_last_log.close()
		if not _last_log:
			_last_rep_file = ''
		return _last_log
 
def log_only_locks(use_cache=FALSE):
	global _last_rep_file, _last_log_with_history, _last_log
	if use_cache and _last_rep_file == vcs.repositoryFile:
		_last_log.reuse()
		return _last_log
	else:
		if string.find(vcs.repositoryFile, ' ') > 0:
			return Rlog(vcs.repositoryFile, vcs.workingFile, '-h -L')
		_last_rep_file = vcs.repositoryFile
		_last_log_with_history = FALSE
		_last_log = Rlog(vcs.repositoryFile, vcs.workingFile, '-h -L')
		_last_log.close()
		if not _last_log:
			_last_rep_file = ''
		return _last_log
 
def log_with_history(use_cache=FALSE):
	global _last_rep_file, _last_log_with_history, _last_log
	if use_cache and _last_log_with_history and (_last_rep_file == vcs.repositoryFile):
		_last_log.reuse()
		return _last_log
	else:
		if string.find(vcs.repositoryFile, ' ') > 0:	# multiple files
			return Rlog(vcs.repositoryFile, vcs.workingFile)
		_last_rep_file = vcs.repositoryFile
		_last_log_with_history = TRUE
		_last_log = Rlog(vcs.repositoryFile, vcs.workingFile)
		_last_log.close()
		if not _last_log:
			_last_rep_file = ''
		return _last_log

def getRevision():
	rev = vcs.symbolName
	if rev[:4] in ('HEAD', 'INIT', '') or string.find(rev,':') >= 0:
		rev = vcs.revision
	return rev

# ---- VCS commands ----------------------------------

def CheckOut():
	force = ''
	lock = ''
	if vcs.forceFlag:
		force = '-f'
	if vcs.lockType == 'E':
		lock = '-l'
	rev = getRevision()
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	repFile = vcs.normpath(vcs.repositoryFile)
	args = (lock, rev, force, repFile, path)
	rc = vcs.system( 'co -V5 %s -q%s %s %s %s' % args)
	if rc:
		return FALSE
	if vcs.lockType == 'C':
		chmod(path, 'u+w')
	return vcs.test(rc)


def CheckOutProject():
	return TRUE


def CreateRepositoryFile():
	if vcs.symbolName:
		symName = string.join(string.split(vcs.symbolName,":"),"_")
		S = '-n'+symName
	else:
		S = ''
	if vcs.lockType == 'E': 
		L = '-l'
	else:
		L = '-u'
	desc = vcs.escape_quotes(vcs.description)
	if not desc:
		desc = '*** empty description ***'
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	args = (desc, vcs.normpath(vcs.repositoryFile), path)
	rc = vcs.system('rcs -i -t-"%s" %s %s' % args)
	if rc==0:
		args = (vcs.revision, L, vcs.escape_quotes(vcs.comment), S, 
				vcs.normpath(vcs.repositoryFile), path)
		rc = vcs.system('ci -V5 -q%s -f %s -m"%s" %s %s %s' % args)
	return vcs.test(rc)


def CheckIn():
	V = vcs.revision
	B = ''
	if vcs.branchFlag:
		log = log_with_history(TRUE)
		if not log: return FALSE
		if not V:
			locks = string.split(log.lockers())
			for i in range(0,len(locks)-1,2):
				if locks[i][:-1] == vcs.user:
					V = locks[i+1]
					break
		rev = log.revision(V)
		if not rev:
			V = log.head_rev()
			rev = log.revision(V)
		if rev:
			i = string.rfind(rev.branches,'.')
			if i > 0:
				next = string.atoi(rev.branches[i+1:-1])+1
			else:
				next = 1
			B = '.' + str(next)
	if vcs.forceFlag:
		N = '-N'
	else:
		N = '-n'
	if vcs.symbolName:
		symName = string.join(string.split(vcs.symbolName,":"),"_")
		S = N+symName
	else:
		S = ''
	if vcs.branchName:
		BR = N+vcs.branchName
	else:
		BR = ''
	if vcs.lockType == 'E': 
		L = '-l'
	else:
		L = '-u'
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	args = (V, B, L, vcs.escape_quotes(vcs.comment), S, BR, 
				vcs.normpath(vcs.repositoryFile), path)
	rc = vcs.system('ci -V5 -q%s%s -f %s -m"%s" %s %s %s %s' % args)
	return vcs.test(rc)


def Lock():
	rc = vcs.system('rcs -V5 -q -l%s %s' % (vcs.revision, vcs.normpath(vcs.repositoryFile)))
	if rc == 0:
		chmod(vcs.joinpath(vcs.workingDir,vcs.workingFile), 'u+w')
	return vcs.test(rc)


def SetWorkFileReadOnly():
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	chmod(path, 'ugo-w')
	return TRUE


def UpdateProject():
	return TRUE
 
_dont_touch_private_files = os.environ.has_key("SNiFF_DONT_TOUCH_PRIVATE_FILES")

def UpdateFile():
	archive = vcs.normpath(vcs.repositoryFile)
	workfile = vcs.joinpath(vcs.workingDir, vcs.workingFile)
	rev = getRevision()
	CO_CMD = 'co -V5 -q -r%s -f "%s" %s' % (rev, archive, workfile)
	DIFF_CMD = 'rcsdiff -V5 -kkv -q -r%s "%s"' % (rev, archive)
 
	if not os.path.exists(archive):
		vcs.output('File @B%s@B has no repository file (%s)' % (vcs.workingFile, archive))
		return FALSE
 
	wf_stat = vcs.get_stat(workfile)
	if vcs.test_writable(wf_stat):
		if vcs.SSWS:
			if os.path.islink(workfile):
				return TRUE
			if _dont_touch_private_files:
				return TRUE
			vcs.output('Touching private writable file @B%s@B' % vcs.workingFile)
			vcs.touch(workfile)
		return TRUE
 
	# workfile is not writable
 
	if SYSTEM==UNIX:
		if vcs.test_mode(wf_stat, 0022):
			vcs.output('Warning: Private file @B%s@B writable by other user' % vcs.workingFile)
			return TRUE
 
	if not vcs.SSWS:
		# project not shared
		rc = vcs.system('%s %s> %s' % (DIFF_CMD, workfile, NULLDEV))
		if rc == 1:
			vcs.output('-> Checking @B%s@B out' % vcs.workingFile)
			rc = vcs.system(CO_CMD)
			return (rc==0)
		elif rc == 2:
			return FALSE
		return TRUE
 
	# project is shared
 
	if TEST: print 'Project is shared'
	SSWSlist = string.split(vcs.SSWS, DELIMITER)
	if not vcs.test_exists(wf_stat):
		if TEST: print 'Private file does not exist'
		shared_file = ''
		for SSWS in SSWSlist:
			shared_file = vcs.joinpath(SSWS, vcs.catDir, vcs.workingFile)
			if os.path.exists(shared_file):
				break
			shared_file = ''
		if not shared_file:
			# shared file does not exist
			if TEST: print 'Shared file does not exist'
			vcs.output('-> Checking @B%s@B out' % vcs.workingFile)
			rc = vcs.system(CO_CMD)
			return (rc==0)
		# shared file exists
		rc = vcs.system('%s %s > %s' % (DIFF_CMD, shared_file, NULLDEV))
		if rc == 1:
			if TEST: print 'Shared file is not up-to-date'
			vcs.output('-> Checking @B%s@B out' % vcs.workingFile)
			rc = vcs.system(CO_CMD)
			return (rc==0)
		elif rc == 2:
			return FALSE
		if TEST: print 'Shared file is up-to-date'
		return TRUE
 
	# file exists in local WS
 
	if TEST: print 'Private file exists'
	shared_file = ''
	for SSWS in SSWSlist:
		shared_file = vcs.joinpath(SSWS, vcs.catDir, vcs.workingFile)
		sf_stat = vcs.get_stat(shared_file)
		if vcs.test_exists(sf_stat):
			if SYSTEM==WINDOWS or not os.path.islink(shared_file):
				break
		shared_file = ''
	if not shared_file:
		# shared file does not exist
		if TEST: print 'Shared file does not exist'
		rc = vcs.system('%s %s> %s' % (DIFF_CMD, workfile, NULLDEV))
		if rc == 1:
			vcs.output('-> Checking @B%s@B out' % vcs.workingFile)
			rc = vcs.system(CO_CMD)
			return (rc==0)
		elif rc == 2:
			return FALSE
		return TRUE
 
	# shared file exists
 
	if TEST: print 'Shared file:', shared_file
	rc = vcs.system('%s %s> %s' % (DIFF_CMD, shared_file, NULLDEV))
	if rc == 1:
		# shared file is not up-to-date
		if TEST: print 'Shared file is not up-to-date'
		rc = vcs.system('%s %s > %s' % (DIFF_CMD, workfile, NULLDEV))
		if rc == 1:
			vcs.output('-> Checking @B%s@B out' % vcs.workingFile)
			rc = vcs.system(CO_CMD)
			return (rc==0)
		elif rc == 2:
			return FALSE
		if os.path.islink(workfile):
			return TRUE
		# private file is up-to-date but not shared file
		if TEST: print 'Private file is up-to-date'
		if _dont_touch_private_files:
			return TRUE
		vcs.output('Touching private read-only file @B%s@B' % vcs.workingFile)
		vcs.touch(workfile)
		return TRUE
	elif rc == 2:
		return FALSE
 
	#shared file is up-to-date - remove local
 
	if TEST: print 'Shared file is up-to-date'
	if os.path.islink(workfile):
		return TRUE
	rc = vcs.rm_f(workfile)
	if rc == 0:
		vcs.output('Private file @B%s@B is deleted, shared file is used' % vcs.workingFile)
		return TRUE
	vcs.output('Private file @B%s@B cannot be deleted' % vcs.workingFile)
	return FALSE


def Unlock():
	args = (vcs.revision, vcs.normpath(vcs.repositoryFile))
	rc = vcs.system('rcs -V5 -q -u%s %s' % args)
	return vcs.test(rc)


def BreakLock():
	args = (vcs.user+' broke the lock with SNiFF+.',
			vcs.revision, vcs.normpath(vcs.repositoryFile)) #, yes_file)
	rc = vcs.sh('( echo y; echo %s ) | rcs -V5 -I -u%s %s' % args)
	return vcs.test(rc)


def DeleteVersion():
	if not vcs.revision:
		log = log_without_history(TRUE)
		if not log:
			return FALSE
		V = log.head
	else:
		V = vcs.revision
	rc = vcs.system('rcs -V5 -q -o%s %s' % (V, vcs.normpath(vcs.repositoryFile)))
	return vcs.test(rc)


def SetDescription():
	args = (vcs.escape_quotes(vcs.description), vcs.normpath(vcs.repositoryFile))
	rc = vcs.system('rcs -V5 -q -t-"%s" %s' % args)
	return vcs.test(rc)


def SetSymbol():
	if vcs.forceFlag:
		S = '-N'
	else:
		S = '-n'
	V = string.strip(vcs.revision) # vcs.revision sometimes ends with newline
	symName = string.join(string.split(vcs.symbolName,":"),"_")
	args = (S, symName, V, vcs.normpath(vcs.repositoryFile))
	rc = vcs.system('rcs -V5 -q %s%s:%s %s' % args)
	return vcs.test(rc)


def DeleteSymbol():
	args = (vcs.symbolName, vcs.normpath(vcs.repositoryFile))
	rc = vcs.system('rcs -V5 -q -n%s %s' % args)
	return vcs.test(rc)


def ChangeComment():
	args = (vcs.revision, vcs.escape_quotes(vcs.comment), vcs.normpath(vcs.repositoryFile))
	rc = vcs.system('rcs -V5 -q -m%s:"%s" %s' % args)
	return vcs.test(rc)


def Description():
	log = log_with_history(TRUE)
	if not log:
		return FALSE
	vcs.output(log.description)
	return TRUE


def History():
	log = log_with_history(TRUE)
	if not log:
		return FALSE
	vcs.output(log.history_str())
	return TRUE
 

def Head():
	log = log_without_history(TRUE)
	if not log:
		return FALSE
	vcs.output(log.head_rev())
	return TRUE


def SymbolList():
	log = log_with_history(FALSE)
	if not log:
		return FALSE
	out_list = []
	while log:
		out_list.append('\014%s\t%s' % (log.work_file(), log.lockers()))
		out_list.append('HEAD: %s' % log.head_rev())
		symbols = log.symbolstr()
		if symbols:
			out_list.append(symbols)
		for rev in log.revision_list():
			out_list.append(string.join((rev.rev, rev.date, rev.author, rev.lines)))
		log.next()
	vcs.output(string.join(out_list, NL))
	return TRUE


def RevisionOfSymbol():
	symbol = vcs.symbolName
	log = log_without_history(TRUE)
	if not log:
		return FALSE
	sym_defs = log.symbollist() 
	rev = ''
	for sym_def in sym_defs:
		[sym_name, version] = string.split(sym_def,':')
		if sym_name == symbol:
			rev = string.lstrip(version)
			break
	if rev:
		vcs.output(rev)
		return TRUE
	else:
		return FALSE


def HistoryOfRevision():
	log = log_with_history(TRUE)
	if not log:
		return FALSE
	rev = log.revision(vcs.revision)
	if not rev:
		return FALSE
	vcs.output(str(rev))
	return TRUE


def Comment():
	log = log_with_history(TRUE)
	if not log:
		return FALSE
	rev = log.revision(vcs.revision)
	if not rev:
		return FALSE
	vcs.output(rev.log_message)
	return TRUE


def Lockers():
	log = log_only_locks(FALSE)
	out_list = []
	processed = {}
	while log:
		processed[log.work_file()] = None
		out_list.append('%s %s' % (log.work_file(), log.lockers()))
		log.next()
	wfiles = string.split(vcs.workingFile)
	for wfile in wfiles:
		if not processed.has_key(wfile):
			out_list.append(wfile)
	vcs.output(string.join(out_list, NL))
	return TRUE


def IsLockedBy():
	cmd = 'rlog -V5 -L -R -l%s %s' % (vcs.user, vcs.normpath(vcs.repositoryFile))
	out = vcs.getoutput(cmd)
	return out != ''


def IsLocked():
	cmd = 'rlog -V5 -L -R %s' % vcs.normpath(vcs.repositoryFile)
	out = vcs.getoutput(cmd)
	return out != ''


def IsDifferent():
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	ksubst = ''
	if SYSTEM==WINDOWS and vcs.is_writable(path):
		ksubst = '-kkvl'
	rev = getRevision()
	args = (ksubst, rev, vcs.normpath(vcs.repositoryFile), path)
	rc = vcs.system("rcsdiff -V5 -q %s -r%s -b %s %s" % args )
	return vcs.test(rc)


def DiffText_W_V():
	path = vcs.joinpath(vcs.workingDir,vcs.workingFile)
	ksubst = ''
	if vcs.is_writable(path):
		ksubst = '-kkvl'
	vcs.revision = vcs.revision2
	rev = getRevision()
	args1 = (ksubst, rev, vcs.normpath(vcs.repositoryFile), 
				vcs.normpath(vcs.tmpFile))
	args2 = (path, vcs.tmpFile)
	vcs.system('co -V5 %s -q%s -f %s %s' % args1)
	out = vcs.getoutput('diff -b %s %s' % args2)
	vcs.output(out)
	return out == ''


def DiffText_V_V():
	args = (vcs.revision, vcs.revision2, vcs.normpath(vcs.repositoryFile))
	out = vcs.getoutput('rcsdiff -V5 -q -r%s -r%s -b %s' % args)
	vcs.output(out)
	return out == ''


def RepositoryDir():
	vcs.output(vcs.joinpath(vcs.RCCSPath, vcs.catDir, 'RCS'))
	return TRUE

def RepositoryFile():
	vcs.output(vcs.joinpath(vcs.repositoryDir, vcs.workingFile + ',v'))
	return TRUE

def TempDir():
	vcs.output(vcs.joinpath(vcs.OSTempDir,'.sniffdir2','RCS_%s%s' % (vcs.user, vcs.pid)))
	return TRUE

def TempFile():
	vcs.output(vcs.joinpath(vcs.tmpDir, vcs.workingFile))
	return TRUE

def BranchDef():
	vcs.output('I')
	return TRUE

def RepositoryCheckDef():
	vcs.output('I')
	return TRUE

def CheckRepositoryDir():
	return TRUE

def CheckRepositoryFile():
	return TRUE

def DateOfRepositoryFile():
	return TRUE


def CreateRepositoryDir():
	rc = vcs.mkdir_p(vcs.repositoryDir, 0775)
	return vcs.test(rc)


def TestTool():
	return vcs.is_reachable('rcs')

def Constructor():
	if not os.path.isdir(vcs.tmpDir):
		vcs.mkdir_p(vcs.tmpDir)
	return TRUE


def Destructor():
	if os.path.isdir(vcs.tmpDir):
		vcs.rm_fr(vcs.tmpDir)
	return TRUE


